import re
import sys
import logging
from threading import Thread, Condition, Event, Lock
from getpass import getpass
from _logging import log_event

_LOGGER = logging.getLogger("cloudcontrol.io")
_io_lock = Lock()

def get_plaintext_input(prompt):
    _io_lock.acquire()
    try:
        return raw_input(prompt)
    finally:
        _io_lock.release()

def get_password_input(prompt):
    _io_lock.acquire()
    try:
        return getpass(prompt)
    finally:
        _io_lock.release()

def write_stdout(message):
    _io_lock.acquire()
    try:
        sys.stdout.write(message)
    finally:
        _io_lock.release()

def write_stderr(message):
    _io_lock.acquire()
    try:
        sys.stderr.write(message)
    finally:
        _io_lock.release()


class Expecter(object):
    
    def __init__(self):
        self.__respond_callbacks = {}
        self.__cv = Condition()
    
    def handle(self, buffer_str, stdin):
            
        if len(self.__respond_callbacks.keys()):
        
            # we have some responders, try them
            log_event(_LOGGER.debug, "trying_to_handle_potential_prompt", {"buffer": buffer_str})
            used_pattern = None
            self.__cv.acquire()
            try:
                for pattern, callback in self.__respond_callbacks.iteritems():
                    if pattern.search(buffer_str):
                        log_event(_LOGGER.debug, "responding_to_expected_prompt", {"responder": callback.__doc__})
                        callback(buffer_str, stdin)
                        used_pattern = pattern
                        return True
            finally:
                if used_pattern:
                    del self.__respond_callbacks[used_pattern]
                self.__cv.release()
            log_event(_LOGGER.debug, "no_responses")
            return False
        
        else:
        
            # we don't have any responders to bother with
            log_event(_LOGGER.info, "no_responders_for_prompts")
            return False
        
    def _add_responder(self, pattern, callback):
        log_event(_LOGGER.info, "adding_responder", {"description": callback.__doc__})
        self.__cv.acquire()
        try:
            self.__respond_callbacks[pattern] = callback
            log_event(_LOGGER.info, "added_responder", {"num_total_responders": len(self.__respond_callbacks)})
            self.__cv.notifyAll()
        finally:
            self.__cv.release()
        log_event(_LOGGER.info, "added_responder", {"num_total_responders": len(self.__respond_callbacks)})
    
    def expect_input_for(self, pattern):
        re_pattern = re.compile(pattern)
        def callback(buffer_str, stdin):
            input_str = get_plaintext_input(prompt = buffer_str)
            stdin.write(input_str)
        callback.__doc__ = "expect input for %(pattern)s" % locals()
        self._add_responder(re_pattern, callback)
    
    def expect_password_for(self, pattern):
        re_pattern = re.compile(pattern)
        def callback(buffer_str, stdin):
            input_str = get_password_input(prompt = buffer_str)
            stdin.write(input_str)
        callback.__doc__ = "expect password for %(pattern)s" % locals()
        self._add_responder(re_pattern, callback)
    
    def respond_with_input_for(self, pattern, plaintext):
        re_pattern = re.compile(pattern)
        def callback(buffer_str, stdin):
            stdin.write(plaintext)
        callback.__doc__ = "respond with %(plaintext)s for %(pattern)s" % locals()
        self._add_responder(re_pattern, callback)
    
    def respond_with_password_for(self, pattern, password):
        re_pattern = re.compile(pattern)
        hidden_password = "*"*len(password)
        def callback(buffer_str, stdin):
            stdin.write(password)
        callback.__doc__ = "respond with %(hidden_password)s for %(pattern)s" % locals()
        self._add_responder(re_pattern, callback)
    